home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / win / whttpd14.zip / CGI-SRC / IMAGEMAP / IMAGEMAP.C next >
C/C++ Source or Header  |  1994-12-18  |  14KB  |  486 lines

  1. // tabs = 4
  2. //------------------------------------------------------------------------
  3. // TITLE:        IMAGEMAP.C
  4. //
  5. // FACILITY:    Image Mapper
  6. //
  7. // ABSTRACT:    This program is intended to serve as a "back end" to a
  8. //                world-wide web server. It takes the name of a "map" and
  9. //                a set of coordinates, and returns a URL (document address)
  10. //                specific to the coordinates and the information in the map.
  11. //
  12. //                The maps themselves are in separate files. The maps are
  13. //                listed in a configuration file called "imagemap.cnf" 
  14. //                that is located on the path given by the environment 
  15. //                variable HTTPD_CONFDIR, defaulting to "c:/httpd/conf".
  16. //                Each entry in this file must be for the form:
  17. //
  18. //                <mapname> : <mapfile pathname>
  19. //
  20. //                Note that this provides a logical to physical mapping facility
  21. //                for the mapfiles. The mapfiles themselves list regions in the 
  22. //                target bitmap as follows:
  23. //
  24. //                <rtype> <URL> <coords>
  25. //
  26. //                where <rtype> is "rect", "poly", "ellipse" or "circle",
  27. //                <URL> is the URL of the document to return if the testpoint
  28. //                is within that region, and <coords> are the defining x-y
  29. //                coordinates    for that type of region.
  30. //
  31. //                NOTE: The <rtype> can also be "default", in which case the URL 
  32. //                is the one to send if the test point is not in any of the 
  33. //                regions. In this case, no coordinates are needed.
  34. //
  35. //
  36. // ENVIRONMENT:    Microsoft Windows 3.1/3.11 (16-bit)
  37. //                Developed under Borland C++ 4.0
  38. //
  39. // AUTHOR:        Bob Denny <rdenny@netcom.com>
  40. //
  41. // Edit Log:
  42. //
  43. // When            Who        What
  44. //----------    ---        --------------------------------------------------
  45. // 21-Nov-94    rbd        Adapted from Kevin Hughes & Casey Barton version,
  46. //                        and the version I made that used doubles.
  47. //                        Convert to use ints and the Windows built-in 
  48. //                        region handling functions.
  49. // 17-Dec-94    rbd        Normalize rects before doing the test so that
  50. //                        corners may be given in any order.
  51. //------------------------------------------------------------------------
  52.  
  53. #include <stdio.h>
  54. #include <string.h>
  55. #include <stdlib.h>
  56. #include <ctype.h>
  57. #include <windows.h>
  58. #include "util.h"
  59.  
  60. #define DEFAULT_CONF_DIR "c:\\httpd\\conf"
  61. #define CONF_FILE_NAME "imagemap.cnf"
  62.  
  63. #define MAXLINE 500
  64. #define MAXVERTS 100
  65. #define X 0
  66. #define Y 1
  67. #define END_SIGNAL 0xFFFFFFFF
  68.  
  69. static char *bad_tgt_msg = "Your client probably doesn't support image maps.";
  70. static char *ofile;
  71. static BOOL fDebug = FALSE;
  72.  
  73. void sendmesg(char *url);
  74. void servererr(char *msg);
  75. void debug_wait(void);
  76. BOOL pointinrect(int point[2], int coords[MAXVERTS][2]);
  77. BOOL pointincircle(int point[2], int coords[MAXVERTS][2]);
  78. BOOL pointinpoly(int point[2], int pgon[MAXVERTS][2], int nvert);
  79. BOOL pointinellipse(int point[2], int coords[MAXVERTS][2]);
  80. static void NormalizeRect(RECT *rp);
  81.  
  82. //========================================================================
  83. // From httpd (windows CGI 1.1):
  84. //
  85. //        argv[1]        CGI .INI file pathname
  86. //        argv[2]        Input file (does not exist for Imagemap)
  87. //        argv[3]        Output file
  88. //        argv[4]        Coordinates
  89. //
  90. //========================================================================
  91. #pragma argsused
  92. int main(int argc, char *argv[])
  93. {
  94.     char input[MAXLINE], mapname[MAXLINE], def[MAXLINE], conf[256];
  95.     int testpoint[2], pointarray[MAXVERTS][2];
  96.     int i, j, k;
  97.     FILE *fp;
  98.     char *t, *cp;
  99.     MSG msg;
  100.  
  101.     //
  102.     // Yield to the system for a bit so the server has a chance
  103.     // to synchronize with our exit...
  104.     //
  105.     for(i=0; i<10; i++)
  106.         PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  107.     
  108.     //
  109.     // Locate the configuration file via this environment variable.
  110.     //
  111.     if((t = getenv("HTTPD_CONFDIR")) != NULL)
  112.         strcpy(conf, t);
  113.     else
  114.         strcpy(conf, DEFAULT_CONF_DIR);
  115.  
  116.     i = strlen(conf) - 1;
  117.     if((conf[i] != '/') && (conf[i] != '\\'))
  118.         strcat(conf, "\\");                        // Assure trailing slash
  119.     strcat(conf, CONF_FILE_NAME);                // Full path to our config file
  120.  
  121.     //
  122.     // Run according to fDebugging mode
  123.     //
  124.     GetPrivateProfileString("System", "Debug Mode", "No", 
  125.                 input, MAXLINE, argv[1]);
  126.     if(tolower(input[0]) == 'y')
  127.     {
  128.         printf("Debugging mode set by server.\n");
  129.         fDebug = TRUE;
  130.     }
  131.  
  132.     //
  133.     // If web server wants back-end debugging, install an atexit() handler
  134.     // that holds the console open until a key is pressed
  135.     //
  136.     if(fDebug)
  137.         atexit(debug_wait);
  138.     
  139.     //
  140.     // The "Logical Path"  (URL extension) contains the map name.
  141.     //    
  142.     GetPrivateProfileString("CGI", "Logical Path", "", 
  143.                 input, MAXLINE, argv[1]);
  144.     strcpy(mapname, &input[1]);            // Skip leading slash
  145.     if(mapname[0] == '\0')                // No map name?
  146.         servererr(bad_tgt_msg);            // Client doesn't support imagemapping
  147.  
  148.     //
  149.     // argv[3] (and the "Output File") contains the name of the file
  150.     // into which we put the result (a "Location:" document or an error message).
  151.     //
  152.     ofile = argv[3];
  153.  
  154.     //
  155.     // Get target coordinates. The only requirement for syntax is
  156.     // that there be two numeric strings acceptable to strtol()
  157.     // and that they be separated by ONE CHARACTER that is not.
  158.     // Browsers SHOULD send "x,y". Use base=0 so strtol() can 
  159.     // deal with decimal, octal and hex values.
  160.     //
  161.     cp = argv[4];
  162.     if(cp == NULL)                        // Missing arg = access violation
  163.         servererr(bad_tgt_msg);            // Bogus URL, no doubt
  164.     testpoint[X] = strtol(cp, &t, 0);    // Attempt to convert X
  165.     if(t == cp)
  166.         servererr(bad_tgt_msg);
  167.     
  168.     cp = t + 1;
  169.     testpoint[Y] = strtol(cp, &t, 0);    // Attempt to convert y
  170.     if(t == cp)
  171.         servererr(bad_tgt_msg);
  172.  
  173.     if(fDebug)
  174.         printf("Map = %s\nOutput = %s\nCoord = [%d,%d]\n",
  175.             mapname, ofile, testpoint[X], testpoint[Y]);
  176.  
  177.     //
  178.     // Open the config file and find the line that matches the map
  179.     // name given in the URL extension.
  180.     // 
  181.     if ((fp = fopen(conf,"r")) == NULL)
  182.         servererr("Couldn't open imagemap config. file.");
  183.  
  184.     while(!(getline(input, MAXLINE, fp))) {
  185.         char buf[MAXLINE];
  186.         
  187.         if((input[0] == '#') || (!input[0]))        // # lines are comments
  188.             continue;
  189.         
  190.         for(i=0; !isspace(input[i]) && (input[i] != ':'); i++)
  191.             buf[i] = input[i];
  192.         
  193.         buf[i] = '\0';
  194.         
  195.         if(!strcmp(buf, mapname))                    // Preserve mapname case
  196.             break;
  197.     }
  198.     
  199.     if(feof(fp)) {
  200.         char buf[256];
  201.  
  202.         sprintf(buf, "Map \"%s\" not found in configuration file.", mapname);
  203.         fclose(fp);
  204.         servererr(buf);
  205.     }
  206.     fclose(fp);
  207.  
  208.     while(isspace(input[i]) || input[i] == ':') ++i;    // Skip past ":" on index line
  209.  
  210.     //
  211.     // Now input[i] -> physical pathname for the mapfile. Collect it and 
  212.     // open the mapfile.
  213.     // 
  214.     for(j=0;input[i] && !isspace(input[i]);++i,++j)
  215.         conf[j] = input[i];
  216.     conf[j] = '\0';
  217.  
  218.     if((fp=fopen(conf,"r")) == NULL)
  219.         servererr("Couldn't open map file.");
  220.  
  221.     //
  222.     // Here's where we read in the regions, URLs and defining coordinates
  223.     // and for each region, perform the hit test for that region type. 
  224.     //
  225.     while(!(getline(input,MAXLINE,fp))) {
  226.         char type[MAXLINE];
  227.         char url[MAXLINE];
  228.  
  229.         if((input[0] == '#') || (!input[0]))        // Skip comment lines
  230.             continue;
  231.  
  232.         type[0] = '\0';url[0] = '\0';
  233.  
  234.         for(i=0; !isspace(input[i]) && (input[i]); i++)    // Get the type
  235.             type[i] = input[i];
  236.         type[i] = '\0';
  237.  
  238.         while(isspace(input[i])) ++i;
  239.  
  240.         for(j=0; input[i] && !isspace(input[i]); ++i, ++j)// Get the URL
  241.             url[j] = input[i];
  242.         url[j] = '\0';
  243.  
  244.         if(!stricmp(type, "default")) {
  245.             strcpy(def, url);
  246.             continue;
  247.         }
  248.  
  249.         //
  250.         // (rbd) Use the features of strtol() to scan off coordinate pairs
  251.         //
  252.         k = 0;                        // Indexes coordinate pairs
  253.         cp = &input[i];                // Switch to pointer
  254.         while (*cp != '\0')
  255.         {
  256.             pointarray[k][X] = (int)strtol(cp, &t, 0);
  257.             if(t == cp)                // No number converted
  258.             {
  259.                 if(*cp != '\0')        // If not at end yet
  260.                     cp += 1;        // Skip past this stopper
  261.                 continue;            // Try again, stop if end of string
  262.             }
  263.             cp = t + 1;                // Skip past stopper (should be ",")
  264.             pointarray[k][Y] = (int)strtol(cp, &t, 0);
  265.             if(t == cp)                // If no Y, this is bad.
  266.             {
  267.                 char buf[256];
  268.  
  269.                 fclose(fp);
  270.                 sprintf(buf,
  271.                     "Missing Y value in map file %s<P>offending line: %s<P>",
  272.                     conf, input);
  273.                 servererr(buf);
  274.             }
  275.             if(*t != '\0')
  276.                 cp = t + 1;
  277.             else
  278.                 cp = t;
  279.             k += 1;
  280.         }
  281.  
  282.         //
  283.         // If sendmesg() is called, it never returns. It exit()s.
  284.         //
  285.         pointarray[k][X] = END_SIGNAL;        // Add signal value
  286.  
  287.         if(!strcmpi(type,"poly"))
  288.             if(pointinpoly(testpoint, pointarray, k))
  289.                 sendmesg(url);
  290.  
  291.         if(!strcmpi(type,"circle"))
  292.             if(pointincircle(testpoint, pointarray))
  293.                 sendmesg(url);
  294.  
  295.         if(!strcmpi(type,"ellipse"))
  296.             if(pointinellipse(testpoint, pointarray))
  297.                 sendmesg(url);
  298.  
  299.         if(!strcmpi(type,"rect"))
  300.             if(pointinrect(testpoint, pointarray))
  301.                 sendmesg(url);
  302.     }
  303.  
  304.     //
  305.     // If we get here, the testpoint was not in any of the regions.
  306.     // Send the default, unless we didn't get one, in which case, 
  307.     // send an error message.
  308.     //
  309.     if(def[0])
  310.         sendmesg(def);
  311.     servererr("No default specified.");
  312.  
  313.     //NOTREACHED
  314.     return(0);            // Shut compiler up
  315. }
  316.  
  317. //=============================================================================
  318. //
  319. //    sendmesg() - Return the URL for the selected region.
  320. //
  321. //=============================================================================
  322. void sendmesg(char *url)  // Output destination URLs directly to OUTPUT_FILE
  323. {
  324.     FILE *outfile;
  325.  
  326.     if(fDebug)
  327.         printf("Resolved to:\n %s\n", url);
  328.  
  329.     if ((outfile = fopen(ofile,"w")) == NULL)
  330.     {
  331.         printf("Couldn't open output file.");        // This is ugly!
  332.         exit(-1);
  333.     }
  334.     fprintf(outfile,"Location: %s%c%c",url,10, 10);
  335.     fprintf(outfile,
  336.         "This document has moved <A HREF=\"%s\">here</A>%c", url, 10);
  337.     fclose(outfile);
  338.     exit(0);
  339. }
  340.  
  341.  
  342. //=============================================================================
  343. //
  344. //    servererr() - Return an HTTP error message.
  345. //
  346. //=============================================================================
  347. void servererr(char *msg) // Output server errors directly to OUTPUT_FILE
  348. {
  349.     FILE *outfile;
  350.  
  351.     if(fDebug)
  352.         printf("An error occurred:\n %s\n", msg);
  353.  
  354.     if ((outfile = fopen(ofile,"w")) == NULL)
  355.     {
  356.         printf("Couldn't open output file.");
  357.         exit(-1);
  358.     }
  359.     fprintf(outfile,"Content-type: text/html%c%c",10,10);
  360.     fprintf(outfile,"<title>Mapping Server Error</title>");
  361.     fprintf(outfile,"<h1>Mapping Server Error</h1><HR>");
  362.     fprintf(outfile,"This server encountered an error:<p>");
  363.     fprintf(outfile,"<code>%s</code>", msg);
  364.     fclose(outfile);
  365.     exit(-1);
  366. }
  367.  
  368.  
  369. //=============================================================================
  370. //
  371. //    sendmesg() - Return the URL for the selected region.
  372. //
  373. //=============================================================================
  374. void debug_wait(void)
  375. {
  376.     char buf[32];
  377.  
  378.     printf("\nPress [enter] to exit...");
  379.     fflush(stdout);
  380.     gets(buf);
  381. }
  382.  
  383. //=============================================================================
  384. //
  385. //    pointinrect() - Return TRUE if point is in rectangle
  386. //
  387. //    Rectangle is defined as top,left bottom,right
  388. //
  389. //=============================================================================
  390. BOOL pointinrect(int point[2], int coords[MAXVERTS][2])
  391. {
  392.     RECT r;
  393.     POINT p;
  394.  
  395.     p.x = point[0];
  396.     p.y = point[1];
  397.  
  398.     SetRect(&r, coords[0][X], coords[0][Y], coords[1][X], coords[1][Y]);
  399.     NormalizeRect(&r);
  400.     return(PtInRect(&r, p));
  401. }
  402.  
  403. //=============================================================================
  404. //
  405. //    pointincircle() - Return TRUE if point is in circle
  406. //
  407. //    For compatibility with old-style maps. Circle is defined as centerpoint,
  408. //    and any point on circumference. For new maps, use ellipse (below).
  409. //
  410. //=============================================================================
  411.  
  412. BOOL pointincircle(int point[2], int coords[MAXVERTS][2])
  413. {
  414.     unsigned int radius1, radius2; 
  415.  
  416.     radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
  417.       + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
  418.  
  419.     radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
  420.       + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
  421.  
  422.     return (radius2 <= radius1);
  423. }
  424.  
  425.  
  426. //=============================================================================
  427. //
  428. //    pointinellipse() - Return TRUE if point is in ellipse
  429. //
  430. //  Ellipse is given by the bounding rectangle top,left bottom,right.
  431. //
  432. //=============================================================================
  433. BOOL pointinellipse(int point[2], int coords[MAXVERTS][2])
  434. {
  435.     RECT r;
  436.     HRGN e;
  437.     BOOL f;
  438.  
  439.     SetRect(&r, coords[0][X], coords[0][Y], coords[1][X], coords[1][Y]);
  440.     NormalizeRect(&r);
  441.     e = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
  442.     f = PtInRegion(e, point[0], point[1]);
  443.     DeleteObject(e);
  444.     return(f);
  445. }
  446.  
  447.  
  448. //=============================================================================
  449. //
  450. //    pointinpoly() - Return TRUE if point is in polygon
  451. //
  452. //  Polygon is given by a series of vertices (x,y). WARNING: Complex
  453. //    overlapping polygons may not act like you think. See the docs on 
  454. //    SetPolyFillMode() for more info. This function is intended to be
  455. //    used on non-overlapping polygons, and will work fine for them.
  456. //
  457. //=============================================================================
  458. int pointinpoly(int point[2], int pgon[MAXVERTS][2], int nvert)
  459. {
  460.     HRGN p = CreatePolygonRgn((POINT FAR *)pgon, nvert, ALTERNATE);
  461.     BOOL f = PtInRegion(p, point[0], point[1]);
  462.     DeleteObject(p);
  463.     return(f);
  464. }
  465.  
  466.  
  467. //=============================================================================
  468. //
  469. //    NormalizeRect() - Assure topleft is really left and above
  470. //
  471. //=============================================================================
  472. static void NormalizeRect(RECT *rp)
  473. {
  474.     int i, j;
  475.  
  476.     if(rp->left > rp->right)
  477.     {
  478.         i = rp->left;
  479.         j = rp->top;
  480.         rp->left = rp->right;
  481.         rp->top = rp->bottom;
  482.         rp->right = i;
  483.         rp->bottom = j;
  484.     }
  485. }
  486.